package logic

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"laundry-service/entity"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"time"

	"github.com/wechatpay-apiv3/wechatpay-go/core"
	wxutils "github.com/wechatpay-apiv3/wechatpay-go/utils"

	"laundry-service/app/client/internal/svc"
	"laundry-service/app/client/internal/types"

	"github.com/shopspring/decimal"
	"github.com/zeromicro/go-zero/core/logx"
)

type WxCreateOrderLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewWxCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WxCreateOrderLogic {
	return &WxCreateOrderLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *WxCreateOrderLogic) createWxRequest(order *entity.Order, ua *entity.UserAuth) map[string]interface{} {
	amount, _ := decimal.NewFromFloat(order.RealAmount * 100).Round(2).Float64()
	mapInfo := map[string]interface{}{
		"mchid":        l.svcCtx.Config.WxPay.MchID,
		"out_trade_no": order.Number,
		"appid":        l.svcCtx.Config.WX.AppID,
		"description":  "衣物",
		"notify_url":   l.svcCtx.Config.WX.GoodsNotifyUrl,
		"amount": map[string]interface{}{
			"total":    amount,
			"currency": "CNY",
		},
		"payer": map[string]interface{}{
			"openid": ua.AuthId,
		},
	}
	fmt.Print(mapInfo)
	return mapInfo
}

func CreateNonceStr(length int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < length; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

func makeRetrunMsg(body []byte, appid string, key string) (resp *types.PaysignReply) {
	var prepaymap map[string]string
	_ = json.Unmarshal(body, &prepaymap)
	// 时间戳
	timeStamp := strconv.FormatInt(time.Now().Unix(), 10)
	// 任意长度的随机字符串即可
	nonceStr := CreateNonceStr(6)
	packageStr := "prepay_id=" + prepaymap["prepay_id"]

	sb := strings.Builder{}
	sb.WriteString(appid + "\n")
	sb.WriteString(timeStamp + "\n")
	sb.WriteString(nonceStr + "\n")
	sb.WriteString(packageStr + "\n")

	privateKey, err := wxutils.LoadPrivateKeyWithPath(key)
	if err != nil {
		log.Println("load err:", err)
	}
	sign, err := wxutils.SignSHA256WithRSA(sb.String(), privateKey)
	if err != nil {
		log.Println("sign error :", err)
	}
	rsp := new(types.PaysignReply)
	rsp.SignType = "RSA"
	rsp.TimeStamp = timeStamp
	rsp.NonceStr = nonceStr
	rsp.Package = packageStr
	rsp.PaySign = sign
	rsp.Prepay_id = prepaymap["prepay_id"]
	return rsp
}

func (l *WxCreateOrderLogic) finsiLogic(response *core.APIResult, requestErr error) (resp *types.PaysignReply, err error) {
	if requestErr != nil {
		fmt.Printf("client post err:%s", err)
		return nil, requestErr
	}
	// 校验回包内容是否有逻辑错误
	requestErr = core.CheckResponse(response.Response)
	if requestErr != nil {
		fmt.Printf("check response err:%s", err)
		return nil, requestErr
	}
	// 读取回包信息
	body, requestErr := io.ReadAll(response.Response.Body)
	if requestErr != nil {
		fmt.Printf("read response body err:%s", err)
		return nil, requestErr
	}
	return makeRetrunMsg(body, l.svcCtx.Config.WX.AppID, l.svcCtx.Config.WxPay.PrivateKeyPath), nil
}

func (l *WxCreateOrderLogic) BusisniessLogic(req *types.OrderCreateReq) (resp *types.PaysignReply, err error) {
	userId, _ := l.ctx.Value("userId").(json.Number).Int64()
	ua, _ := l.svcCtx.Db.GetUserAuthByUserId(int(userId))

	order, err := l.svcCtx.Db.GetOrderById(req.OrderId)
	if err != nil {
		return nil, errors.New("订单不存在")
	}
	if order.UserId != int(userId) {
		return nil, errors.New("暂不可为该订单支付")
	}

	client, err := core.NewClient(l.ctx, l.svcCtx.Opt...)
	if err != nil {
		fmt.Printf("init client err:%s", err)
		return nil, err
	}

	URL := "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
	return l.finsiLogic(client.Post(l.ctx, URL, l.createWxRequest(order, ua)))
}

func (l *WxCreateOrderLogic) MockPayment() (resp *types.PaysignReply, err error) {
	userId, _ := l.ctx.Value("userId").(json.Number).Int64()
	ua, err := l.svcCtx.Db.GetUserAuthByUserId(int(userId))
	if err != nil {
		return nil, err
	}

	fmt.Println("user auth:", ua.AuthId, ua.UserId)

	mock := new(entity.Order)
	mock.Amount = 0.01
	mock.RealAmount = 0.01
	mock.Number = "zmel_test_v1"

	client, err := core.NewClient(l.ctx, l.svcCtx.Opt...)

	if err != nil {
		fmt.Printf("init client err:%s", err)
		return nil, err
	}
	URL := "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
	return l.finsiLogic(client.Post(l.ctx, URL, l.createWxRequest(mock, ua)))
}

func (l *WxCreateOrderLogic) WxCreateOrder(req *types.OrderCreateReq) (resp *types.PaysignReply, err error) {
	if req.Mock == "zmel_test_v1" {
		return l.MockPayment()
	} else {
		return l.BusisniessLogic(req)
	}
}
